/*-------------------------------------------------------------------------
 *
 * sinval.h
 *	  POSTGRES 共享缓存失效通信定义。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/storage/sinval.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef SINVAL_H
#define SINVAL_H

#include <signal.h>

#include "storage/relfilenode.h"

/*
 * 我们支持几种类型的共享失效消息：
 *	* 使特定 catcache 中的特定元组失效
 *	* 使给定系统目录中的所有 catcache 条目失效
 *	* 使特定逻辑关系的 relcache 条目失效
 *	* 使所有 relcache 条目失效
 *	* 使特定物理关系的 smgr 缓存条目失效
 *	* 使给定数据库的映射关系映射失效
 *	* 使任何可能用于扫描给定关系的保存快照失效
 * 如果需要，可以添加更多类型。消息类型通过消息结构的第一个 "int8" 字段来识别。零或正值表示特定 catcache 的失效消息（同时作为 catcache ID 字段）。负值根据下面的代码标识其他消息类型。
 *
 * Catcache 失效事件最初是通过检测系统目录中的元组插入、
 * 更新和删除（见 CacheInvalidateHeapTuple）来驱动的。
 * 更新可以生成两个失效事件，一个用于旧元组，一个用于新元组，
 * 但如果元组的哈希键没有改变，将减少为一个事件。注意，
 * 失效事件本身并没有实际说明元组是被插入还是被删除。
 * 此外，由于我们仅传输哈希键，因此由于哈希键的偶然匹配而造成的不必要失效风险很小。
 *
 * 注意某些系统目录上有多个缓存（具有不同的索引）。
 * 在检测到此类目录中的元组失效后，必须为其每个缓存生成单独
 * 的 catcache 失效消息，因为哈希键通常会不同。
 *
 * Catcache、relcache 和快照失效是事务性的，因此在提交时会
 * 发送给其他后端。在生成后端内部，它们还会在 CommandCounterIncrement
 * 时处理，以便同一事务中的后续命令看到新状态。生成后端
 * 还必须在中止时处理它们，以清除其从不再有效的条目加载的任何缓存状态。
 *
 * smgr 和关系映射失效是非事务性的：它们在底层文件变更时
 * 立即发送。
 */

typedef struct
{
	int8		id;				/* 缓存 ID --- 必须在最前面 */
	Oid			dbId;			/* 数据库 ID，或 0 表示共享关系 */
	uint32		hashValue;		/* 此 catcache 的键的哈希值 */
} SharedInvalCatcacheMsg;

#define SHAREDINVALCATALOG_ID	(-1)

typedef struct
{
	int8		id;				/* 类型字段 --- 必须在最前面 */
	Oid			dbId;			/* 数据库 ID，或 0 表示共享目录 */
	Oid			catId;			/* 内容失效的目录 ID */
} SharedInvalCatalogMsg;

#define SHAREDINVALRELCACHE_ID	(-2)

typedef struct
{
	int8		id;				/* 类型字段 --- 必须在最前面 */
	Oid			dbId;			/* 数据库 ID，或 0 表示共享关系 */
	Oid			relId;			/* 关系 ID，或 0 表示整个 relcache */
} SharedInvalRelcacheMsg;

#define SHAREDINVALSMGR_ID		(-3)

typedef struct
{
	/* 注意：字段布局选择为打包为 16 字节 */
	int8		id;				/* 类型字段 --- 必须在最前面 */
	int8		backend_hi;		/* 后端 ID 的高位，如果是 temprel */
	uint16		backend_lo;		/* 后端 ID 的低位，如果是 temprel */
	RelFileNode rnode;			/* spcNode, dbNode, relNode */
} SharedInvalSmgrMsg;

#define SHAREDINVALRELMAP_ID	(-4)

typedef struct
{
	int8		id;				/* 类型字段 --- 必须在最前面 */
	Oid			dbId;			/* 数据库 ID，或 0 表示共享目录 */
} SharedInvalRelmapMsg;

#define SHAREDINVALSNAPSHOT_ID	(-5)

typedef struct
{
	int8		id;				/* 类型字段 --- 必须在最前面 */
	Oid			dbId;			/* 数据库 ID，或 0 表示共享关系 */
	Oid			relId;			/* 关系 ID */
} SharedInvalSnapshotMsg;

typedef union
{
	int8		id;				/* 类型字段 --- 必须在最前面 */
	SharedInvalCatcacheMsg cc;
	SharedInvalCatalogMsg cat;
	SharedInvalRelcacheMsg rc;
	SharedInvalSmgrMsg sm;
	SharedInvalRelmapMsg rm;
	SharedInvalSnapshotMsg sn;
} SharedInvalidationMessage;


/* 已处理的消息计数；不必担心溢出。 */
extern PGDLLIMPORT uint64 SharedInvalidMessageCounter;

extern PGDLLIMPORT volatile sig_atomic_t catchupInterruptPending;

extern void SendSharedInvalidMessages(const SharedInvalidationMessage *msgs,
									  int n);
extern void ReceiveSharedInvalidMessages(void (*invalFunction) (SharedInvalidationMessage *msg),
										 void (*resetFunction) (void));

/* 用于捕捉事件的信号处理器 (PROCSIG_CATCHUP_INTERRUPT) */
extern void HandleCatchupInterrupt(void);

/*
 * 直接从信号处理程序启用/禁用追赶事件的处理。
 * 启用例程首先处理自上次禁用以来发生的任何追赶事件。
 */
extern void ProcessCatchupInterrupt(void);

extern int	xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
												 bool *RelcacheInitFileInval);
extern void ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs,
												 int nmsgs, bool RelcacheInitFileInval,
												 Oid dbid, Oid tsid);

extern void LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg);

#endif							/* SINVAL_H */
